<?
/*
 * Copyright (c) 2008 Sergey Bondari, bondari@bondari.net
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * - Redistributions of source code must retain the above copyright
 *   notice, this list of conditions and the following disclaimer.
 * - Redistributions in binary form must reproduce the above copyright
 *   notice, this list of conditions and the following disclaimer in the
 *   documentation and/or other materials provided with the distribution.
 * - The name of the author may not be used to endorse or promote products
 *   derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

namespace Mosaic\System;


/**
 * Remote object stub base class
 * 
 * Any remote object stub should be derived from this class.
 * It implements client side GRT algorith, parameters marshaling and 
 * object binding.
 *
 * @package System
 * @subpackage Node
 */
class RemoteObjectStub {
	/**
	 * Binding information string, aka remote object URI
	 * @var string
	 */
	private $__uri;

	/**
	 * Constructor arguments for defered constructor call
	 * @var array Array of paramters
	 */
	private $constructorArguments;

	/*
	 * Binded status flag indicator
	 * @var boolean True if object has binded as the implicite binding is lazy.
	 */
	private $__binded;

	/**
	 * GRT generation number
	 * @var integer
	 */
	private $__grtGen;

	/**
	 * GRT copy counter
	 * @var integer
	 */
	private $__grtCpy;


	/**
	 * Serialization GRT generation. This generation must be set upon deserialization. 
	 * @var integer
	 */
	private $__serializationGRTGeneration;

	/**
	 * Node, object was serialized on. Deserialization on this node triggers explicite binding
	 * @var string
	 */
	private $__serializationNodeSource;

	/**
	 * Stub constructor. Records constructor arguments for deferred implicit binding.
	 */
	final function __construct() {
		$this->__grtGen = 0;
		$this->__grtCpy = 0;
		$this->constructorArguments = func_get_args();
	}

	/**
	 * Binding status
	 * @return boolean
	 */
	final public function __isBinded() {
		return $this->__binded;
	}


	/**
	 * Gets object URI. Triggers binding if uri is not set.
	 * @return string URI
	 */
	final public function __getUri() {
		if ($this->__uri) return $this->__uri;
		$this->_r_construct($this->constructorArguments);
		return $this->__uri;
	}

	/**
	 * Explicitely binds to the existing object instance.
	 * @param string URI
	 */
	final public function __bindUri($uri) {
		$req = new RemoteObjectCallRequest($uri, '__bind', array($this->__grtGen, $this->__grtCpy));
		$req->execSync();
		$this->__uri = $uri;
		$this->__binded = 1;
	}

	/**
	 * Remote constructor call
	 * @param array Constructor arguments
	 */
	protected function _r_construct($args) {
		$req = new RemoteObjectInstantiateRequest(get_class($this), $args);
		$uri = $req->execSync();
		$this->__uri = $uri;
		$this->__binded = 1;
	}

	/**
	 * Remote destructor.
	 * Triggers unbinding.
	 */
	protected function _r_destruct() {
		$req = new RemoteObjectCallRequest($this->__getUri(), '__unbind', array($this->__grtGen, $this->__grtCpy));
		$req->execAsync();
	}

	/**
	 * Remote method call
	 * @param string Method name
	 * @param array Method call arguments
	 * @return mixed Method return value
	 */
	protected function _r_val_call($method, $args) {
		$req = new RemoteObjectCallRequest($this->__getUri(), $method, $args);
		return $req->execSync();
	}

	/**
	 * Remote property getter
	 * @param string Property name
	 * @return mixed
	 */
	protected function _r_get($key) {
		$req = new RemoteObjectCallRequest($this->__getUri(), '__get', array($key));
		$req->execSync();
	}

	/**
	 * Remote property get
	 * @param string Property name
	 * @param mixed Property value
	 */
	protected function _r_set($key, $val) {
		$req = new RemoteObjectCallRequest($this->__getUri(), '__set', array($key, $val));
		$req->execSync();
	}


	/**
	 * Automatic property setter PHP wrapper. Calls remote property access.
	 * @param string
	 * @param mixed
	 */
	public function __set($key, $value) {
		$this->_r_set($key, $value);
	}

	/**
	 * automatic property setter PHP wrapper. Calls remote property access
	 * @param string
	 */
	public function __get($key) {
		$this->_r_get($key);

	}

	/**
	 * Destructor wrapper
	 * Calls GRT unbind if binded.
	 */
	function __destruct() {
		if ($this->__binded) {
			$this->_r_destruct();
		}
	}


	/**
	 * After serialization 
	 *
	 * Serialization basically clones the object: stub side GRT algorithm implementation.
	 *
	 * @return array Array of properties to serialize
	 */
	function __sleep() {
		global $sys;
		$this->__grtCpy += 1;
		$this->__serializationGRTGeneration = $this->__grtGen+1;
		$this->__serializationNodeSource = $sys->name;

		return array("\0".get_class()."\0__uri", "\0".get_class()."\0__serializationGRTGeneration", "\0".get_class()."\0__serializationNodeSource", "\0".get_class()."\0__binded", "\0".get_class()."\0constructorArguments");
	}


	/**
	 * After deserialization 
	 *
	 * Reinitialize GRT data arrived from another node. Force object unbind object if deserialized on the same node.
	 *
	 * @return void
	 */
	function __wakeup() {
		global $sys;
		if ($this->__serializationGRTGeneration && $sys->name != $this->__serializationNodeSource) {
			$this->__grtGen = $this->__serializationGRTGeneration;
			$this->__grtCpy = 0;

		} else {
			$this->__binded = 0;
			$this->__grtGen = 0;
			$this->__grtCpy = 0;
		}

		$this->__serializationGRTGeneration = NULL;
		$this->__serializationNodeSource = NULL;

	}

}

